# @Time    : 2022/9/23 10:31
# @Author  : 南黎
# @FileName: 4.分析高斯核函数中核参数的设置对模型性能的影响.py

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from sklearn import preprocessing
from sklearn.datasets import load_iris  # 导入鸢尾花数据集
from sklearn.svm import SVC  # 使用支持向量机算法


# 画出数据点和边界
def border_of_classifier(sklearn_cl, x, y):
    """
    param sklearn_cl : skearn 的分类器
    param x: np.array
    param y: np.array
    """
    ## 1 生成网格数据
    x_min, y_min = x.min(axis=0) - 1
    x_max, y_max = x.max(axis=0) + 1
    # 利用一组网格数据求出方程的值，然后把边界画出来。
    x_values, y_values = np.meshgrid(np.arange(x_min, x_max, 0.01),
                                     np.arange(y_min, y_max, 0.01))
    # 计算出分类器对所有数据点的分类结果 生成网格采样
    mesh_output = sklearn_cl.predict(np.c_[x_values.ravel(), y_values.ravel()])
    # 数组维度变形
    mesh_output = mesh_output.reshape(x_values.shape)
    fig, ax = plt.subplots(figsize=(16, 10), dpi=80)
    ## 会根据 mesh_output结果自动从 cmap 中选择颜色
    plt.pcolormesh(x_values, y_values, mesh_output, cmap='rainbow')
    plt.scatter(x[:, 0], x[:, 1], c=y, s=100, edgecolors='steelblue', linewidth=1, cmap=plt.cm.Spectral)
    plt.xlim(x_values.min(), x_values.max())
    plt.ylim(y_values.min(), y_values.max())
    # 设置x轴和y轴
    plt.xticks((np.arange(np.ceil(min(x[:, 0]) - 1), np.ceil(max(x[:, 0]) + 1), 1.0)))
    plt.yticks((np.arange(np.ceil(min(x[:, 1]) - 1), np.ceil(max(x[:, 1]) + 1), 1.0)))



# 封装一个模型训练函数，输入数据文件名即可完成训练
def my_train(datafile, myGamma):
    data = pd.read_csv(datafile).values
    # 数据归一化
    # max_abs_scaler = preprocessing.MaxAbsScaler()  # 注册一个预处理对象
    # data = max_abs_scaler.fit_transform(data)  # fit_transform(X[, y])	适合数据，然后转换它。
    X = data[:, 0:2]
    y = data[:, 2].astype('int')

    clf = SVC(C=1, kernel='rbf',gamma=myGamma, decision_function_shape='ovr')  # 内核类型为rbf
    # C：浮动，可选（默认=1.0）误差项的惩罚参数 C。
    # kernel：指定要在算法中使用的内核类型。它必须是“linear”、“poly”、“rbf”、“sigmoid”、“precomputed”或可调用对象之一。如果没有给出，将使用 'rbf'。如果给定了可调用对象，则它用于从数据矩阵中预先计算内核矩阵；该矩阵应该是一个 shape 数组(n_samples, n_samples)。
    # linear：选择线性函数；
    # polynomial：选择多项式函数；
    # rbf：选择径向基函数；
    # sigmoid：选择 Logistics 函数作为核函数；
    # precomputed：使用预设核值矩阵，
    # SVC 类默认以径向基函数作为核函数。
    # decision_function_shape :默认决策函数分界线形状 ‘ovo’, ‘ovr’ or None, default=None
    # gamma: 高斯核函数,浮动，可选（默认='auto'）'rbf'、'poly' 和 'sigmoid' 的核系数。如果 gamma 为“auto”，则将使用 1/n_features。
    # fit_intercept：布尔值，默认值 真：指定是否应将常数（也称为偏差或截距）添加到决策函数中。

    # 训练模型,使用fit喂入数据X,y，即特征值和标签
    clf.fit(X, y)
    # 预测分类
    result = clf.predict(X)
    # 对模型进行评分
    score = clf.score(X, y)
    print(datafile + "rbf内核SVM评分", score)
    plt.figure()
    # 分割图1行1列第一个图
    plt.subplot(111)
    # 选择X特征1和特征2进行绘图
    # 原有的数据绘图
    plt.scatter(X[:,0],X[:,1],c =y.reshape((-1)),edgecolor='k',s=50)
    # 预测结果的绘图
    # plt.scatter(X[:, 0], X[:, 1], c=result.reshape((-1)), edgecolor='k', s=50)
    plt.savefig("4/" + "gamma={}{}文件原始数据图.png".format(myGamma,datafile))
    plt.show()
    border_of_classifier(clf, X, y)
    plt.savefig("4/" + "gamma={}{}{}".format(myGamma,datafile,"rbf内核SVM分类图.png"))
    plt.show()


my_train("data1_ch5.csv", myGamma=0.01)
my_train("data1_ch5.csv", myGamma=10)
my_train("data1_ch5.csv", myGamma=1000)